/*
 * SCanvas.java
 *
 * Created on 29 de diciembre de 2006, 02:43 PM
 *
 *Created by zur
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package hello;

import java.io.IOException;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.rms.*;
import javax.microedition.lcdui.*;
import java.util.Stack;

public class SCanvas extends GameCanvas implements Runnable  {
    private final int widthRequired = 114;
    private final int heightRequired = 96;
    private int sizeEdgeImage = 6;
    private int realWidth = getWidth();
    private int realHeight = getHeight();
    private int currentLevel;
    private boolean controlWhile = true;
    private boolean stateExit = true;
    private boolean colisionSoko;
    private boolean keypress;
    private sokobanSprite sokoban;
    private boxSprite[] boxArray;
    private LayerManager layerManager;
    private Board mapWorld;
    private Sokoban refSoko;
    private String levelRecord = "levelWinned";
    private String ruteBox = "/hello/boxSprite.png";
    private String ruteSoko = "/hello/sokoSprites.png";
    private int delayTime = 300;
    private Command restartCommand;
    private Stack stackundosoko = new Stack();
    private Stack stackundobox = new Stack();
    private int [] posprueba= new int [2];
    
    /** Creates a new instance of SCanvas */
    public SCanvas(Sokoban reference) {
        super(true);
        this.refSoko = reference;
        this.setFullScreenMode(false);
    }
    
    private void createBoxArray(int numBox,int [][] positionBoxMatriz) throws IOException {
        int size = this.getSizeEdgeImage();
        Image image = Image.createImage(ruteBox);
        boxArray = new boxSprite[numBox];
        for(int i=0; i<numBox; i++){
            boxArray[i]=new boxSprite(image,size,size);
            boxArray[i].setPosicion(positionBoxMatriz[i][0]*size, positionBoxMatriz[i][1]*size);
            boxArray[i].setDelta(size);
            layerManager.append(boxArray[i]);
        }
    }
    
    private sokobanSprite createSokoban() throws IOException {
        int sizeX = this.getSizeEdgeImage();
        int sizeY = this.getSizeEdgeImage();
        Image image = Image.createImage(ruteSoko);
        return new sokobanSprite(image, sizeX, sizeY);
    }
    
    public String[] loadNivel() {
        int i=0;
        String [] levelsArray={};
        byte[] data;
        String s;
        try{
            RecordStore rs = RecordStore.openRecordStore(this.levelRecord,true);
            RecordEnumeration re = rs.enumerateRecords(null,null,false);
            levelsArray = new String [re.numRecords()*2];
            while(re.hasNextElement()){
                data = re.nextRecord();
                s = new String(data);
                levelsArray[2*i] = s.substring(0,s.indexOf("#"));
                levelsArray[2*i+1] = s.substring(s.indexOf("#")+1,s.length());
                i++;
            }
            rs.closeRecordStore();
        } catch(RecordStoreNotOpenException e) { } catch(RecordStoreException e) {  }
        return levelsArray;
    }
    
    void startGame(int level) {
        int size = this.getSizeEdgeImage();
        int pX = 0, pY = 1;
        this.controlWhile = true;
        if (level==0){
            currentLevel = loadLevel();
        } else{
            currentLevel = level;
        }
        int[] posSoko = new int[2];
        int[][] positionBoxMatriz;
        layerManager = new LayerManager();
        mapWorld = new Board(currentLevel,size);
        layerManager.append(mapWorld.getLayerWorld());
        try{
            posSoko = mapWorld.getPositionSoko();
            sokoban = createSokoban();
            sokoban.setPosicion(posSoko[pX]*size,posSoko[pY]*size);
            sokoban.setDelta(size);
            sokoban.setFrame(7);
            
            layerManager.append(sokoban);
            positionBoxMatriz = mapWorld.getPositionBoxes();
            createBoxArray(positionBoxMatriz.length,positionBoxMatriz);
            layerManager.append(mapWorld.getBackground());
            Graphics g = getGraphics();
            render(g);
        } catch(Exception ioex){
            Graphics g = getGraphics();
            g.drawString("There's no image.", 50, 50,Graphics.TOP|Graphics.LEFT );
        }
        flushGraphics();
    }
    
    public void run(){
        Graphics g = getGraphics();
        while(controlWhile){
            input();
            tick();
            render(g);
            endGame();
            try {
                Thread.sleep(this.getDelayTime());
            } catch(InterruptedException ie){
                controlWhile = false;
            }
        }
        notifyExit(this.stateExit);
    }
    
    private void tick(){
        int size = this.getSizeEdgeImage();
        colisionSoko = false;
        int xc=0,yc=0;
        boolean apilo = false;
        if(sokoban.collidesWith(mapWorld.getLayerWorld(),false)){
            sokoban.setPosicion(sokoban.getLastPosx(), sokoban.getLastPosy());
            colisionSoko=true;
        }
        for(int i=0;i<boxArray.length;i++){
            if (sokoban.collidesWith(boxArray[i],false)) {
                xc = sokoban.getPosx()- sokoban.getLastPosx();
                yc = sokoban.getPosy()- sokoban.getLastPosy();
                int [] pos = {i,boxArray[i].getPosx(),boxArray[i].getPosy()};
                if (xc!=0){
                    boxArray[i].moveHorizontal(xc/size);
                }
                if (yc!=0)
                    boxArray[i].moveVertical(yc/size);
                
                if (boxArray[i].collidesWith(mapWorld.getLayerWorld(),false)){
                    boxArray[i].setPosicion(boxArray[i].getLastPosx(), boxArray[i].getLastPosy());
                    sokoban.setPosicion(sokoban.getLastPosx(), sokoban.getLastPosy());
                    colisionSoko=true;
                }
                for(int j=0; j<boxArray.length;j++){
                    if(j!=i)
                        if(boxArray[i].collidesWith(boxArray[j],false)){
                        
                        boxArray[i].setPosicion(boxArray[i].getLastPosx(), boxArray[i].getLastPosy());
                        sokoban.setPosicion(sokoban.getLastPosx(), sokoban.getLastPosy());
                        colisionSoko=true;
                        }
                }
                if(boxArray[i].collidesWith(mapWorld.getBackground(),false))
                    boxArray[i].setArrived(true);
                if(pos[1]!=boxArray[i].getPosx() || pos[2]!=boxArray[i].getPosy()){
                    stackundobox.push(pos);
                    apilo = true;
                }
            }
        }
        if (colisionSoko==false && keypress){
            sokoban.incrementarPasos();
            int[] pos = {posprueba[0],posprueba[1],sokoban.getFrame()};
            stackundosoko.push(pos);
            if (!apilo){
                stackundobox.push(null);
                
            }
        }
    }
    public boolean getcWhile() {
        return this.controlWhile;
    }
    
    private void input() {
        keypress = false;
        
        int keyStates = getKeyStates();
        if((keyStates & 102) != 0){
            posprueba[0] = sokoban.getPosx();
            posprueba[1] = sokoban.getPosy();
        }
        if ((keyStates & LEFT_PRESSED) != 0){
            sokoban.moverHorizontal(-1);
        } else if ((keyStates & RIGHT_PRESSED) != 0){
            sokoban.moverHorizontal(1);
        } else if ((keyStates & UP_PRESSED) != 0){
            sokoban.moverVertical(-1);
        } else if ((keyStates & DOWN_PRESSED) != 0){
            sokoban.moverVertical(1);
        } else if ((keyStates & 256) != 0) {
            this.controlWhile = false;
            this.stateExit = false;
        }
        
        if ((keyStates & 255) != 0)
            keypress = true;
    }
    
    private void render(Graphics g) {
        int w = realWidth;
        int h = realHeight;
        g.setColor(0xffffff);
        g.fillRect(0, 0, w, h);
        int x = (w - widthRequired) / 2;
        int y = (h - heightRequired) / 2;
        int HeightBoard = mapWorld.getHeightBoard()*mapWorld.getSizeSprite();
        int WidthBoard  = mapWorld.getWidthBoard()*mapWorld.getSizeSprite();

        int x2 = (w/2-WidthBoard/2);
        layerManager.paint(g, x2, y);
        g.setColor(0x000000);
        g.drawRect(x, y, widthRequired, heightRequired);
        g.drawString(sokoban.getPasos(),x+5,y+82,Graphics.LEFT|Graphics.TOP);
        flushGraphics();
    }
    
    public String getCurrentLevel(){
        Integer level;
        level = new Integer(currentLevel);
        return level.toString();
    }
    public void undoSoko(){
        if(!stackundosoko.empty()){
            int [] pos = (int [])stackundosoko.pop();
            sokoban.setPosicion(pos[0],pos[1]);
            sokoban.setFrame(pos[2]);
            sokoban.DecrementarPasos();
            
        }
    }
    public void undobox(){
        if(!stackundobox.empty()){
            int [] pos = (int [])stackundobox.pop();
            if (pos!=null)
                boxArray[pos[0]].setPosicion(pos[1],pos[2]);
        }
    }
    private void endGame() {
        boolean state = true;
        for(int i=0; i<boxArray.length;i++)
            if(boxArray[i].getState() == false)
                state = false;
        
        if(state == true){
            this.controlWhile = false;
            this.stateExit = true;
        }
    }
    
    private void notifyExit(boolean winned) {
        String s;
        byte[] data;
        try{
            if (winned == true){
                RecordStore rs = RecordStore.openRecordStore(this.levelRecord,true);
                int rID = this.searchRecord(rs,this.currentLevel);
                if(rID==-1){
                    s = this.getCurrentLevel();
                    s = s + "#" + sokoban.getPasos().toString();
                    data  = s.getBytes();
                    int recordID = rs.addRecord(data,0,data.length);
                }
                if(updateScore(rs,rID)) {
                    s = this.getCurrentLevel();
                    s = s + "#" + sokoban.getPasos().toString();
                    data  = s.getBytes();
                    rs.setRecord(rID,data,0,data.length);
                }
                rs.closeRecordStore();
            }
        } catch(RecordStoreNotOpenException e) { } catch(RecordStoreException e) { }
        this.refSoko.menuMostar();
    }
    
    private int loadLevel() {
        int level=1;                // value by default
        byte[] data;
        String s;
        try{
            RecordStore rs = RecordStore.openRecordStore(this.levelRecord,true);
            data = rs.getRecord(rs.getNumRecords());
            s = new String(data);
            level = Integer.parseInt(s.substring(0,s.indexOf("#"))) + 1 ;
            rs.closeRecordStore();
        } catch(RecordStoreNotOpenException e) { } catch(RecordStoreException e) {  }
        return level;
    }
    
    private int searchRecord(RecordStore rs, int currentNivel) {
        String s;
        byte[] data;
        try {
            RecordEnumeration re = rs.enumerateRecords(null,null,false);
            while(re.hasNextElement()){
                int rID = re.nextRecordId();
                data = rs.getRecord(rID);
                s = new String(data);
                if (s.substring(0,s.indexOf("#")).equals(String.valueOf(currentNivel)))
                    return rID;
            }
            return -1;
        } catch(RecordStoreNotOpenException e) { } catch(RecordStoreException e) {  }
        return -1;
    }
    
    
    private boolean updateScore(RecordStore rs, int rID) {
        if(rID!=-1){
            String s;
            byte[] data = null;
            int scoreInFile = 0;
            try {
                data = rs.getRecord(rID);
                s = new String(data);
                scoreInFile = Integer.parseInt(s.substring(s.indexOf("#")+1,s.length()));
                if (scoreInFile > Integer.parseInt(sokoban.getPasos())){
                    return true;
                } else {
                    return false;
                }
            } catch (RecordStoreNotOpenException ex) {
                ex.printStackTrace();
            } catch (InvalidRecordIDException ex) {
                ex.printStackTrace();
            } catch (RecordStoreException ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }
    
    public int getSizeEdgeImage() {
        return sizeEdgeImage;
    }
    
    public void setSizeEdgeImage(int sizeEdgeImage) {
        this.sizeEdgeImage = sizeEdgeImage;
    }
    
    public int getDelayTime() {
        return delayTime;
    }
    
    public void setDelayTime(int delayTime) {
        this.delayTime = delayTime;
    }
    
    public void setMapWorld(Board mapWorld) {
        this.mapWorld = mapWorld;
    }
}






